using System;
using System.Collections.Concurrent;

namespace ZerAx.Net
{
    public static class Channel<P>
    {
        public static (Sender, Receiver) Create()
        {
            var recv = new Receiver();
            return (new Sender(recv), recv);
        }

        public class Receiver : IDisposable
        {
            internal ConcurrentQueue<P> queue;
            
            internal Receiver()
            {
                queue = new ConcurrentQueue<P>();
            }

            public bool IsClosed => queue == null;

            internal void Put(P pkt)
            {
                queue.Enqueue(pkt);
            }

            public bool Poll(out P pkt)
            {
                return queue.TryDequeue(out pkt);
            }

            public void Close()
            {
                queue = null;
            }

            public void Dispose()
            {
                Close();
            }
        }

        public class Sender : ICloneable
        {
            private Receiver receiver;

            internal Sender(Receiver receiver)
            {
                this.receiver = receiver;
            }

            public bool IsClosed => receiver == null;

            public Sender Clone()
            {
                return new Sender(receiver);
            }

            public bool Send(P pkt)
            {
                if (receiver == null || receiver.IsClosed)
                {
                    receiver = null;
                    return false;
                }
                receiver.Put(pkt);
                return true;
            }

            object ICloneable.Clone()
            {
                return Clone();
            }
        }
    }
}